答疑大杂烩

# 答疑大杂烩

[TOC]

# 1. 一次性插入1000个div,如何优化插入的性能
  • 使用Fragment

    • var fragment = document.createDocumentFragment();
      fragment.appendChild(elem);
      
      1
      2
  • 向1000个并排的div元素中,插入一个平级的div元素,如何优化插入的性能

    • 先display:none 然后插入 再display:block
    • 赋予key,然后使用virtual-dom,先render,然后diff,最后patch
    • 脱离文档流,用GPU去渲染,开启硬件加速
# 2.替换和不可替换元素
  • 替换元素

替换元素就是浏览器根据元素的标签和属性,来决定元素的具体显示内容。

例如浏览器会根据<img> 标签的 src 属性的值来读取图片信息并显示出来,而如果查看 HTML 代码,则看不到图片的实际内容;又例如根据 <input> 标签的 type 属性来决定是显示输入框,还是单选按钮等。

HTML 中的<img> 、<input>、<textarea> 、<select> 、<object>都是替换元素。这些元素往往没有实际的内容,即是一个空元素。

浏览器会根据元素的标签类型和属性来显示这些元素。可替换元素也在其显示中生成了框。

  • 不可替换元素

HTML 的大多数元素是不可替换元素,即其内容直接表现。

# 3.不能作为判断条件
  1. 相对路径(绝对路径才可以)
  2. 颜色值
  3. 背景
# 4.中文文字下用word-spacing没有效果?

letter-spacing为每个字符之间的空白距离,word-spacing为单词之间的空白距离,故应使用letter-spacing

# 5.a标签中的href="javascript:;"是什么意思?
  1. javascript: 是一个伪协议。javascript:是表示在触发a标签默认动作时,执行一段Js代码,而javascript:;表示什么都不执行,就是去掉a标签的默认行为(防止页面刷新和跳转),这样点击a标签时就没有任何反应。
  2. href="javascript:void(0)"是一样的,void是Js的一个运算符,void(0)就是什么都不做的意思。
  3. 有了href才支持回车点击和默认可以获取焦点
# 6.overflow:hidden可以实现什么效果?
  1. 父元素无height值:清除别人浮动对自己的影响;子元素浮动时,则在父元素使用overflow: hidden可以清除浮动,使得父元素的高度依旧是靠子元素撑开.
  2. 父元素有height值:隐藏子元素超出父元素的部分。
# 7.使用clear:both要注意什么问题?

使用后margin属性失效。

clear的定义是:元素盒子的边不能与前面的浮动元素相邻。也就是虽然浮动元素高度坍塌,但是设置了clear: both的元素却将其高度视为仍然占据位置。

clear只能作用于块级元素,并且其并不能解决后面元素可能发生的文字环绕问题。

# 8.NPM依赖包版本号
  • ~会匹配1.2.X版本的最新版
  • ^会匹配^1.x.x版本的最新版
  • *匹配最最新版
# 9. CSS3硬件加速

参考教程:在 CSS 动画中使用硬件加速(翻译) (opens new window)

  • 使用css3硬件加速,可以让transform、opacity、filter这些动画不会引起回流重绘。(只有transform、opacity、filter属性变化会直接在GPU处理)

  • 对于动画的其他属性,比如background-color,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。

  • 常见的触发硬件加速的css属性:transform、opacity、filters、will-change;HTML5标签:video、canvas、webgl。

  • 若太多元素用CSS3硬件加速,会导致内存占用较大,存在性能问题。(GPU处理过多的内容会导致内存问题,这在移动端和移动端浏览器会导致崩溃)

  • 如果不在动画结束的时候关闭硬件加速,会产生字体模糊。(因为GPU和CPU的算法不同,因此在动画结束后要关闭硬件加速)

  • 会引发耗电量增加。

  • 性能优化

    • /* 告诉浏览器该属性会发生变化,因此浏览器会在开始之前对其进行优化 */
      .example {
        will-change: transform;
      }
      
      1
      2
      3
      4
# 10.
  • 以下原因可能导致不能自动播放: chrome6以及更高的版本不允许媒体自动播放; safari 阻止自动播放视频。opera 阻止autoplay; 出于用户体验,节省流量的考虑,移动端禁止自动播放; 视频文件太大,加载时间过长或错误。
  • 解决办法:视频元素添加muted 属性,如<video controls muted>
# 11.
# 12.position:sticky失效原因

参考链接:https://juejin.cn/post/6909393635557507086#heading-5

可能的原因有:

1. 包裹的父级容器高度与sticky元素一致。
2. 包裹的父容器设置了`overflow:hidden`。
# 13.floatdisplay: inline-block的使用
  1. 一般情况下,使用floatfloat就是隐性的把内联元素转化为块元素,近似于display:inline-block。为什么不直接display:inline-block,因为显示会有几个px的bug。
  2. float需要清除浮动;display: inline-block则不需要。
  3. display: inline-block默认vertical-align: bottom,可以通过vertical属性设置默认基线;float则不行。
# 14. input为什么多出1px?
  • 参考链接:https://segmentfault.com/q/1010000005367537
# 15. 一个空格的大小&nbsp;
  1. 字体样式不一样,空格大小也不一样。
  2. 只有当前字体为宋体时,空格大小为字体大小的一半。
# 16.opacity:0display: nonevisibility:hidden的区别
  1. 结构

    display:none: 会让元素完全从渲染树中消失,不占空间, 不能点击。 visibility: hidden和opacity: 0:不会让元素从渲染树消失,仍占空间。但visibility: hidden的元素不可点击而opacity: 0可以点击。

  2. 继承 display: none:非继承属性,子孙节点由于祖先元素从渲染树消失而无法显示。 visibility: hidden:继承属性,子孙节点消失由于继承了hidden,通过设置visibility: visible;可以让子孙节点显示。

    opacity:0:继承属性,但子元素并不能通过 opacity: 1 来取消隐藏。

  3. 性能 display: none:修改元素会引起回流,性能消耗较大。 visibility:hidden和opacity: 0 : 修改元素会造成重绘,性能消耗较少。

# 17.["1","2","3"].map(parseInt)
  • 结果: [1, NaN, NaN]
    • parseInt()接收两个参数,第二个参数是进制数。
    • map()在调用callback函数时,会给它传递三个参数(val,index,arr):当前正在遍历的元素,元素索引,原数组本身。
    • 第三个参数parseInt()会无视,第二个索引参数则作为进制数处理。
# 18.字符串不可变,此话怎讲?

当我们想要改变某个变量保存的字符串:它先是销毁了原来的字符串,再用另一个包含新值的字符串填充该变量。

var lang = "Java";
lang = lang + "Script";
1
2
  1. 第一步:创建一个可以容纳10个字符的新字符串。
  2. 第二步:在这个字符串中填充"Java"和"Script"
  3. 第三步:销毁原来的字符串"Java"和"Script",因为已经用不到了,我们需要的是第一步时候创建的新字符串。
# 19.块元素与内联元素嵌套规则
  • 块元素
    • div , p , form, ul, li , ol, dl, form, address, hr, menu, table
  • 行内元素
    • span, strong, em, br,label, select, textarea, cite
  • 行内块
    • img、input、td
  1. <a>实际能嵌套块元素(原则上不能)。
  2. 有几个特殊的块级元素只能包含内联元素,不能再包含块级元素。这几个特殊标签是:h1h2h3h4h5h6pdt
  3. li可以包含div,但不可以包含li
# 20.ECMAScript 自动分号;插入问题
  1. 虽然js不强制使用分号,但实际上它需要分号来解析源代码。
  2. js解析器在遇到由于缺少分号导致的解析错误,会自动在源代码插入分号。
  3. 为了避免解析器猜测错误而导致错误处理,则需要了解插入分号的机制和原理。
    • “必须用分号终止某些 ECMAScript 语句 ( 空语句 , 变量声明语句 , 表达式语句 , do-while 语句 , continue 语句 , break 语句 , return 语句 ,throw 语句 )。这些分号总是明确的显示在源文本里。然而,为了方便起见,某些情况下这些分号可以在源文本里省略。描述这种情况会说:这种情况下给源代码的 token 流自动插入分号。”
    • 建议绝对不要省略分号,同时也提倡将花括号和相应的表达式放在一行, 对于只有一行代码的 if 或者 else 表达式,也不应该省略花括号。 这些良好的编程习惯不仅可以提到代码的一致性,而且可以防止解析器改变代码行为的错误处理。
# 21.为什么css放头部,js放尾部?
  1. CSS 资源阻塞渲染。
    • 构建Render树需要DOM和CSSOM,所以HTML和CSS都会阻塞渲染。所以需要让CSS尽早加载(如:放在头部),以缩短首次渲染的时间。
  2. JS 资源阻塞浏览器的解析。
    • 发现一个外链脚本时,需等待脚本下载完成并执行后才会继续解析HTML。
    • 浏览器中js引擎线程和渲染线程是互斥的,详见《从setTimeout-setInterval看JS线程》 (opens new window)。普通的脚本会阻塞浏览器解析,加上defer或async属性,脚本就变成异步,可等到解析完毕再执行。
      • async异步执行,异步下载完毕后就会执行,不确保执行顺序,一定在onload前,但不确定在DOMContentLoaded事件的前后。
      • defer延迟执行,相对于放在body最后(理论上在DOMContentLoaded事件前)。
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
    <script src="app.js"></script>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11

js资源阻塞解析

  • 浏览器拿到HTML后,从上到下顺序解析文档
  • 此时遇到css、js外链,则同时发起请求
  • 开始构建DOM树
  • 这里要特别注意,由于有CSS资源,CSSOM还未构建前,会阻塞js(如果有的话)
  • 无论JavaScript是内联还是外链,只要浏览器遇到 script 标记,唤醒 JavaScript解析器,就会进行暂停 blocked 浏览器解析HTML,并等到 CSSOM 构建完毕,才执行js脚本
  • 渲染首屏(DOMContentLoaded 触发,其实不一定是首屏,可能在js脚本执行前DOM树和CSSOM已经构建完render树,已经paint)
# 22. ol标签出现不显示序号的情况
  • 导致的原因可能是*{margin:0;padding:0;}
  • 方法一:ol{padding-left: 20px;}
  • 方法二:ol li{list-style-type:decimal;list-style-position:inside;}
# 23.QuerySelector/QuerySelectorAllgetElementById/getElementsByClassName的区别
  1. 兼容性:通过https://caniuse.com/比较,没区别。

  2. 效率:测试发现GEBI和GEBC比QS和QSA要快

    console.time('querySelector'); 
    for (var i = 0; i < 100000; i++) {
        document.querySelector("#test"); 
    } 
    console.timeEnd('querySelector'); 
    
    1
    2
    3
    4
    5
  3. 灵活性:QS/QSA 均支持CSS的选择器(但不支持伪类选择器,更灵活。

    querySelector('div img .test')
    //找到div下面的img下面类名为test的元素
    
    1
    2

4.动态性:通过QSA选择的不受后来DOM变化的影响,但是通过GEBC会受DOM的影响。

a = document.querySelectorAll('img');
b =document.getElementsByTagName('img');
document.body.appendChild(new Image());
console.log(a.length); // 0
console.log(b.length); // 1
1
2
3
4
5
# 24. 结束循环和遍历
  • 数组遍历(break, continue均不合法)
    • forEach:无法结束遍历,只能用return退出本次回调,进行下一次回调。
    • filter:同上。
    • map:同上。
    • reduce,reduceRight:同上。
    • every:只要有元素不满足则返回false,并结束遍历。
    • some:只要有元素满足则返回true,并结束遍历。
    • find、findIndex:找到就停止遍历。
    • keys、values、entries:不接受传参。
  • for循环
    • for:可break,continue,不可return。
    • for...in:同上。
    • for...of:同上。
  • while循环:可break,continue,不可return。
# 25.
# 26.
# 27.
# 28.(function(window,document,undefined){})(window,document);的意义?
  • window, document实参分别接受window, document对象,window, document对象都是全局环境下的,而在函数体内的window, document其实是局部变量,不是全局的。
    • **可以提高性能,减少作用域链的查询时间。**如果你在函数体内需要多次调用window 或 document对象,这样把window 或 document对象当作参数传进去,这样做是非常有必要的。当然如果你的插件用不到这两个对象,那么就不用传递这两个参数了。
  • undefined在老一辈的浏览器是不被支持的,直接使用会报错,JS框架要考虑到兼容性,因此 增加一个形参undefined。
# 29.从URL输入到页面展示,到底发生了什么?

首先通过DNS将URL解析为对应的IP地址;(DNS缓存,DNS prefetch)

然后与IP地址确定的服务器建立TCP连接; (长连接,预连接,接入SPDY协议)

随后向服务器抛出HTTP请求; (减少请求次数,减小请求体积,CDN)

服务端处理请求并发送数据给回客户端;

浏览器解析渲染页面.(资源加载优化,服务端渲染,浏览器缓存机制的利用,DOM树的构建,网页排版和渲染过程,回流与重绘的考量,DOM操作的合理规避)

从HTTP请求回来,就产生了流式的数据,后续的DOM树构建、CSS计算、渲染、合成、绘制,都是尽可能地流式处理前一步的产出:即不需要等到上一步骤完全结束,就开始处理上一步的输出,这样我们在浏览网页时,才会看到逐步出现的页面。

  1. DNS解析

    • 将域名解析成IP地址
  2. TCP连接

    • 三次握手
  3. 发送HTTP请求

  4. 服务器处理请求并返回HTTP报文

  5. 浏览器解析渲染页面

  6. 断开连接

    • 四次挥手
# 30.服务端渲染SSR
  • 服务端渲染本质上是把本该浏览器做的事情,分给服务器去做。
  • 可以用来优化首屏渲染体验和SEO。
  • 但由于服务器稀少而珍贵,所以应该优先考虑其他方案来解决。
# 31.有哪些值是假?
  • 假:0、NaN、''、false、undefined、null(六个假)
// 使用Boolean构造函数过滤数组中的所有假值
const compact = arr => arr.filter(Boolean);
compact([0, NaN, false, '', undefined, null, 'a', 1, [], {}," "]);	// ["a", 1, [], {}, " "]
1
2
3
# 32.避免在不必要的情况下使用eval

eval() 是一个危险的函数, 他执行的代码拥有着执行者的权利。

如果你用 eval() 运行的字符串代码被恶意方(不怀好意的人)操控修改,您最终可能会在您的网页/扩展程序的权限下,在用户计算机上运行恶意代码。

更重要的是

,第三方代码可以看到某一个eval()被调用时的作用域,这也有可能导致一些不同方式的攻击。

相似的 Function 就不容易被攻击。

eval() 通常比替代方法慢,因为它必须调用 JS 解释器,而许多其他结构则由现代 JS 引擎进行优化。

在常见的案例中我们都会找更安全或者更快的方案去替换 eval()。

# 33.
# 34.HTTP请求中 request payload 和 formData 区别?

FormData和Payload是浏览器传输给接口的两种格式,这两种方式浏览器是通过Content-Type来进行区分的(了解Content-Type (opens new window)),如果是 application/x-www-form-urlencoded的话,则为formdata方式,如果是application/json或multipart/form-data的话,则为 request payload的方式。

# 35. 怎么判断一个值是不是NaN?
  • 结论
    • ES6新增的Number.isNaN(NaN)
    • Object.is(NaN, NaN)
    • function valueIsNaN(v) { return v !== v; }
typeof NaN									// “number”
1
  • 不能用等号运算符来判断。
NaN === NaN									// false
NaN == NaN									// false
Number.NaN === NaN; 						// false
1
2
3
  • isNaN只要不是number就会返回true。
isNaN(NaN);         						// true
isNaN(Number.NaN);  						// true
isNaN('A String'); 							// true
isNaN(undefined); 							// true
isNaN({}); // true
1
2
3
4
5
  • ES6新增Number.isNaN(),只有NaN才返回true。
Number.isNaN(NaN); 							// true
Number.isNaN('A String'); 					// false
Number.isNaN(undefined);					// false
Number.isNaN({});							// false
1
2
3
4
  • 数字中只有NaN不等于自身。
function valueIsNaN(v) { return v !== v; }
valueIsNaN(1);          					// false
valueIsNaN(NaN);      						// true
valueIsNaN(Number.NaN); 					// true

// 以下比较运算符两边是两个不同的实例
[] !== []									// true
{} !== {}									// true
// 以下则比较的是同一个实例
valueIsNaN([])								// false
valueIsNaN({})								// false
1
2
3
4
5
6
7
8
9
10
11
  • ES6新增,与严格相等运算符行为基本一致,除了以下两种。
Object.is(NaN, NaN);						// true
Object.is(-0, +0);							// false
NaN === NaN									// false
-0 === +0									// true
1
2
3
4
# 36. 良好的CSS选择器书写习惯

因为CSS选择符是从右到左进行匹配的,比如#id li是先遍历页面所有的li元素,再去确认li的父级是不是#id

因此在书写CSS选择器的时候,应该尽可能让浏览器少遍历元素。

  • 避免使用通配符,通配符会导致浏览器需要遍历每一个元素。
  • 少用标签选择器。
  • 减少嵌套。
# 37. 在网页中的应该使用奇数还是偶数的字体?

使用偶数字体。偶数字号相对更容易和 web 设计的其他部分构成比例关系。Windows 自带的点阵宋体(中易宋体)从 Vista 开始只提供 12、14、16 px 这三个大小的点阵,而 13、15、17 px时用的是小一号的点。(即每个字占的空间大了 1 px,但点阵没变),于是略显稀疏。

# 38. 数据类型的判断
  1. typeof 返回一个表示数据类型的字符串,返回结果包括:number、boolean、string、symbol、object、undefined、function7种数据类型,但不能判断null、array等。
  2. instanceof 是用来判断A是否为B的实例,表达式为:A instanceof B,如果A是B的实例,则返回true,否则返回false。instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性,但它不能检测null 和 undefined(没有prototype属性)。
  3. Object.prototype.toString.call() 是最准确最常用的方式
    • 每个对象都有一个toString()方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString()方法被每个Object对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 "[object type]",其中type是对象的类型。
    • type类型有:String、Number、Boolean、Undefined、Null、Function、Date、Array、RegExp、Error。
# 39. 怎么处理移动端 1px 被渲染成 2px 的问题?

手机分表率很高的时候,比如二倍屏就会显示2px。

  • width:设置viewport宽度,为一个正整数,或为device-width(设备宽度)。
  • height:设置viewport高度,一般设置了宽度,会自动解析出高度,可以不用设置。
  • initial-scale:默认缩放比例(初始缩放比例),为一个数字,可以带小数。
  • minimum-scale:允许用户最小缩放比例,为一个数字,可以带小数。
  • maximum-scale:允许用户最大缩放比例,为一个数字,可以带小数。
  • user-scalable:是否允许手动缩放。
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
1
  1. 局部处理

    meta标签中的viewport属性,initial-scale设置为1。

    rem按照设计稿走,然后利用transform的scale(0.5)缩小一倍即可。

  2. 全局处理

    meta标签中的viewport属性,initial-scale设置为0.5。

    rem按照设计稿走即可。

# 40. z-index失效
  1. z-index属性只作用在被定位了的元素上。所以如果你在一个没被定位的元素上使用z-index的话,是不会有效果的。
  2. 同一个父元素下的元素的层叠效果会受父元素的z-index影响,如果父元素的z-index值很小,那么子元素的z-index值很大也不起作用。
# 41. 获取路径
//js获取项目根路径,如: http://localhost:8083/uimcardprj
function getRootPath(){
    //获取当前网址,如: http://localhost:8083/uimcardprj/share/meun.jsp
    var curWwwPath=window.document.location.href;
    //获取主机地址之后的目录,如: uimcardprj/share/meun.jsp
    var pathName=window.document.location.pathname;
    var pos=curWwwPath.indexOf(pathName);
    //获取主机地址,如: http://localhost:8083
    var localhostPaht=curWwwPath.substring(0,pos);
    //获取带"/"的项目名,如:/uimcardprj
    var projectName=pathName.substring(0,pathName.substr(1).indexOf('/')+1);
    return(localhostPaht+projectName);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
# 42. Object.create(null){}的区别?

参考链接:详解Object.create(null) (opens new window)

  1. Object.create()的第一个参数是指新创建对象的原型对象,以null作为原型,可以避免继承了Object自身的方法,如hasOwnPropertytoString等。
  2. {}相当于Object.create(Object.prototype)
  3. 需要一个非常干净且高度可定制的对象当作数据字典的时候;或想节省hasOwnProperty带来的一丢丢性能损失并且可以偷懒少些一点代码的时候,就可以用Object.create(null)
# 43. DOM操作为什么是昂贵的?
  • JS引擎和渲染引擎(浏览器内核)是独立实现的。当用JS操作DOM时,本质上是JS引擎和渲染引擎进行了跨界交流。
  • 对DOM修改时若引发样式改变,就会触发回流或重绘。
  • 减少DOM操作的核心思路,就是让JS给DOM分压。
# 44. 获取文本内容的方法
  • element.innerHTML 会直接返回element节点下所有的HTML化的文本内容,只能作用于元素节点调用;文本节点并不能使用这个方法,会返回undefined。
  • nodeValue
<span class="name">lhj</span>
1
// 属性节点的 nodeValue属性返回属性值
$('.name')[0].firstChild.nodeValue	// lhj
// 元素节点的 nodeValue属性返回null
$('.name')[0].nodeValue	// null
1
2
3
4
  • textContent类似innerHTML,在DOM中标签换行产生的空白字符会计入DOM中作为文本节点。
  • innerText不常用。